import pandas as pd
import numpy as np
import matplotlib

# 设置matplotlib使用非交互式后端，避免tkinter相关错误
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib.font_manager import FontProperties

import io
import base64
import platform

# 设置中文字体
try:
    # 根据系统设置字体
    system = platform.system()
    if system == 'Darwin':
        plt.rcParams['font.family'] = 'sans-serif'
        plt.rcParams['font.sans-serif'] = [
            'PingFang SC',  # 苹方-简（macOS 内置）
            'Heiti SC',  # 黑体-简（macOS 内置）
            'Arial Unicode MS',  # 跨平台备选
            'Hiragino Sans GB'  # 冬青黑体（部分 Mac 安装）
        ]
    elif system == 'Linux':
        # 方法1：全局设置（推荐）
        plt.rcParams['font.family'] = 'sans-serif'
        plt.rcParams['font.sans-serif'] = ['WenQuanYi Micro Hei']  # 虽然报错但实际会生效
        plt.rcParams['axes.unicode_minus'] = False
    else:
        plt.rcParams['font.family'] = ['SimHei']  # 其他系统
    plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题
    font_path = "font/SimHei.ttf"  # 自定义路径（需确保文件存在）
    font = FontProperties(fname=font_path)
except:
    pass


class Visualizer:
    @staticmethod
    def plot_to_base64(fig):
        """将matplotlib图形转换为base64编码"""
        buf = io.BytesIO()
        fig.savefig(buf, format='png', dpi=300, bbox_inches='tight')
        buf.seek(0)
        img_str = base64.b64encode(buf.read()).decode('utf-8')
        buf.close()
        plt.close(fig)
        return img_str

    @staticmethod
    def sales_trend(data, title='盲盒销售趋势', save_path=None):
        """销售趋势图"""
        fig, ax = plt.subplots(figsize=(12, 6))

        if isinstance(data.index, pd.DatetimeIndex):
            plot_data = data
        else:
            plot_data = data.set_index('date')

        plot_data['sales_quantity'].plot(ax=ax, linewidth=2)

        ax.set_title(title, fontproperties=font, fontsize=14)
        ax.set_xlabel('日期', fontproperties=font)
        ax.set_ylabel('销售数量', fontproperties=font)

        plt.grid(True, alpha=0.3)
        plt.tight_layout()

        if save_path:
            plt.savefig(save_path)

        # 存储结果并关闭图形，避免内存泄漏和tkinter错误
        result_fig = fig
        plt.close(fig)

        return result_fig

    @staticmethod
    def category_comparison(data, x='category', y='sales_quantity', title='类别销售对比', save_path=None):
        """类别销售对比图"""
        if not isinstance(data, pd.DataFrame):
            raise ValueError("数据必须是pandas DataFrame类型")

        # 按类别聚合
        if 'date' in data.columns:
            agg_data = data.groupby(x)[y].sum().reset_index()
        else:
            agg_data = data.groupby(x)[y].sum().reset_index()

        # 绘制条形图
        fig, ax = plt.subplots(figsize=(10, 6))
        sns.barplot(x=x, y=y, data=agg_data, ax=ax)

        ax.set_title(title, fontproperties=font, fontsize=14)
        ax.set_xlabel('类别', fontproperties=font)
        ax.set_ylabel('销售数量', fontproperties=font)

        plt.xticks(rotation=45)
        plt.tight_layout()

        if save_path:
            plt.savefig(save_path)

        # 存储结果并关闭图形，避免内存泄漏和tkinter错误
        result_fig = fig
        plt.close(fig)

        return result_fig

    @staticmethod
    def forecast_comparison(actual, predictions, title='销售预测对比', save_path=None):
        """实际销售与预测对比图"""
        fig, ax = plt.subplots(figsize=(12, 6))

        # 绘制实际数据
        actual['sales_quantity'].plot(ax=ax, label='实际销售', linewidth=2)

        # 绘制预测数据
        predictions['sales_quantity_pred'].plot(ax=ax, label='预测销售', linewidth=2, linestyle='--')

        ax.set_title(title, fontproperties=font, fontsize=14)
        # ax.set_xlabel('日期', fontproperties=font)
        # ax.set_ylabel('销售数量', fontproperties=font)

        ax.legend(prop=font)
        plt.grid(True, alpha=0.3)
        plt.tight_layout()
        plt.title(title, fontsize=14)
        plt.xlabel('日期', fontsize=12)
        plt.ylabel('销售数量', fontsize=12)
        if save_path:
            plt.savefig(save_path)

        # 存储结果并关闭图形，避免内存泄漏和tkinter错误
        result_fig = fig
        plt.close(fig)

        return result_fig

    @staticmethod
    def seasonality_analysis(seasonal_data, title='季节性分析', save_path=None):
        """季节性模式图"""
        fig, ax = plt.subplots(figsize=(10, 6))

        seasonal_data.plot(ax=ax)

        ax.set_title(title, fontproperties=font, fontsize=14)

        if isinstance(seasonal_data.index[0], str) and len(seasonal_data.index) <= 7:
            # 星期数据
            ax.set_xlabel('星期', fontproperties=font)
        elif isinstance(seasonal_data.index[0], str) and len(seasonal_data.index) <= 12:
            # 月份数据
            ax.set_xlabel('月份', fontproperties=font)
        else:
            ax.set_xlabel('周期', fontproperties=font)

        ax.set_ylabel('平均销售数量', fontproperties=font)

        plt.grid(True, alpha=0.3)
        plt.tight_layout()

        if save_path:
            plt.savefig(save_path)

        # 存储结果并关闭图形，避免内存泄漏和tkinter错误
        result_fig = fig
        plt.close(fig)

        return result_fig

    @staticmethod
    def elasticity_by_category(elasticity_data, title='各类别价格弹性', save_path=None):
        """各类别价格弹性对比图"""
        # 将数据转换为DataFrame
        df = pd.DataFrame({
            'category': elasticity_data.keys(),
            'elasticity': elasticity_data.values()
        })

        # 按弹性绝对值排序
        df['abs_elasticity'] = df['elasticity'].abs()
        df = df.sort_values('abs_elasticity', ascending=False)

        # 添加弹性类型
        df['elasticity_type'] = df['elasticity'].apply(
            lambda x: '富有弹性' if abs(x) > 1 else '缺乏弹性'
        )

        # 设定颜色
        colors = df['elasticity_type'].map({
            '富有弹性': 'red',
            '缺乏弹性': 'blue'
        })

        fig, ax = plt.subplots(figsize=(10, 6))

        bars = ax.bar(df['category'], df['elasticity'], color=colors)

        # 添加参考线
        ax.axhline(y=-1, color='black', linestyle='--', alpha=0.5)
        ax.axhline(y=0, color='black', alpha=0.3)

        # 在柱上添加数值标签
        for bar in bars:
            height = bar.get_height()
            ypos = height + 0.1 if height > 0 else height - 0.2
            ax.text(bar.get_x() + bar.get_width() / 2., ypos,
                    f'{height:.2f}', ha='center', fontsize=9)

        ax.set_title(title, fontproperties=font, fontsize=14)
        ax.set_xlabel('类别', fontproperties=font)
        ax.set_ylabel('价格弹性系数', fontproperties=font)

        # 添加图例
        from matplotlib.lines import Line2D
        legend_elements = [
            Line2D([0], [0], color='red', lw=4, label='富有弹性 (|e| > 1)'),
            Line2D([0], [0], color='blue', lw=4, label='缺乏弹性 (|e| < 1)')
        ]
        ax.legend(handles=legend_elements, prop=font)

        plt.tight_layout()

        if save_path:
            plt.savefig(save_path)

        # 存储结果并关闭图形，避免内存泄漏和tkinter错误
        result_fig = fig
        plt.close(fig)

        return result_fig
